package io.github.chutian0610.sqlparser.dialect

import io.github.chutian0610.sqlparser.token.CodePoint
import kotlin.reflect.KClass

/**
 * SQL Dialects
 */
interface Dialect {

    /**
     * Returns the name of the dialect.
     * The default implementation returns the simple name of the dialect class.
     */
    fun dialectName(): String = this::class.simpleName ?: "Unknown"

    /**
     * Determine if the dialect supports escaping characters via '\' in string literals.
     * Some dialects like BigQuery and Snowflake support this while others like
     * Postgres do not. Such that the following is accepted by the former but
     * rejected by the latter.
     *
     * ```sql
     * SELECT 'ab\'cd';
     * ```
     *
     * Conversely, such dialects reject the following statement which
     * otherwise would be valid in the other dialects.
     *
     * ```sql
     * SELECT '\';
     * ```
     */
    fun supportsStringLiteralBackslashEscape(): Boolean = false

    /**
     * Returns true if the dialect supports numbers containing underscores, e.g. `10_000_000`
     */
    fun supportsNumericLiteralUnderscores(): Boolean = false


    /**
     * Determine if the dialect supports string literals with `U&` prefix.
     * This is used to specify Unicode code points in string literals.
     * For example, in PostgreSQL, the following is a valid string literal:
     * ```sql
     * SELECT U&'\0061\0062\0063';
     * ```
     * This is equivalent to the string literal `'abc'`.
     * See
     *  - [Postgres docs](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-UESCAPE)
     *  - [H2 docs](http://www.h2database.com/html/grammar.html#string)
     */
    fun supportsUnicodeStringLiteral(): Boolean = false


    /** Returns true if this dialect supports the E'...' syntax for string literals.
     * See
     * - [Postgres docs](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-ESCAPE)
     */
    fun supportsStringEscapeConstant(): Boolean = false

    /**
     * Returns true if the dialect supports identifiers starting with a numeric
     * prefix such as tables named `59901_user_login`
     */
    fun supportsNumericPrefix(): Boolean = false

    /**
     * Determine if a character is a valid unquoted identifier character
     */
    fun isIdentifierPart(c: CodePoint): Boolean

    /**
     * Determine if a character is a valid start character for an unquoted identifier
     */
    fun isIdentifierStart(c: CodePoint): Boolean

    /**
     *  Determine if a character starts a quoted identifier. The default
     *  implementation, accepting "double quoted" ids is both ANSI-compliant
     *  and appropriate for most dialects (with the notable exception of
     *  MySQL, MS SQL, and sqlite). You can accept one of characters listed
     *  in [io.github.chutian0610.sqlparser.token.Token.matchingIdentifierEndQuote]
     */
    fun isDelimitedIdentifierStart(c: CodePoint): Boolean = CodePoint.equalsChar(c, '"') || CodePoint.equalsChar(c, '`')


    /** Returns true if this dialect requires a whitespace character after `--` to start a single line comment.
     * ```
     *  UPDATE account SET balance=balance--1
     *  WHERE account_id=5752             ^^^ will be interpreted as two minus signs instead of a comment
     *  ```
     *  see [MySql](https://dev.mysql.com/doc/refman/8.4/en/ansi-diff-comments.html)
     *
     */
    fun requiresSingleLineCommentWhitespace(): Boolean = false

    /**
     * Most dialects do not have custom operators. Override this method to provide custom operators.
     */
    fun isCustomOperatorPart(c: CodePoint): Boolean = false

    /**
     * Returns true if the dialect supports nested comments
     * e.g. `/* /* nested */ */`
     */
    fun supportsNestedComments(): Boolean = false

    /**
     * Returns true if the dialect supports geometric types. e.g. @@ circle '((0,0),10)'.
     *
     * see [Postgres](https://www.postgresql.org/docs/9.5/functions-geometry.html)
     */
    fun supportsGeometricTypes(): Boolean = false

    /**
     * Return true if the dialect supports pipe operator.
     *
     * Example:
     * ```sql
     * SELECT *
     * FROM table
     * |> limit 1
     * ```
     * See <https://cloud.google.com/bigquery/docs/pipe-syntax-guide#basic_syntax>
     */
    fun supportsPipeOperator(): Boolean = false


    /**
     * Returns true if this dialect allows dollar placeholders
     * e.g. `SELECT $var` (SQLite)
     */
    fun supportsDollarPlaceholder(): Boolean = false
}

inline fun <reified T : Dialect> dialectIs(instance: Dialect, type: KClass<T>): Boolean {
    return type.isInstance(instance)
}

inline fun <reified T : Dialect> dialectIn(instance: Dialect, vararg types: KClass<out T>): Boolean {
    return types.any { it.isInstance(instance) }
}